package com.kevin.datasource;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import javax.sql.DataSource;

public class DefaultDataSourceFactory implements DataSourceFactory {

	private static final String DRIVER_PROPERTY_PREFIX = "driver.";
	private static final int DRIVER_PROPERTY_PREFIX_LENGTH = DRIVER_PROPERTY_PREFIX.length();
	  
	protected DataSource dataSource;
	  
	public DefaultDataSourceFactory() {
		this.dataSource = new DefaultDataSource();
	}
	
	public DefaultDataSourceFactory(DataSource dataSource){
		this.dataSource = dataSource;
	}
	
	public DefaultDataSourceFactory(String className){
		try {
			Class<?> druidClass = Class.forName(className);
			this.dataSource = (DataSource)druidClass.getDeclaredConstructor().newInstance();
		} catch (Exception e) {
			throw new DataSourceException(e);
		}	
	}
	
	@Override
	public void setProperties(Properties properties) {
		Properties driverProperties = new Properties();
		
		ArrayList<Method> methods = getDeclaredField(dataSource);
		Map<String, Method> methodMap = new HashMap<>();
		for (Method method : methods) {
			methodMap.put(method.getName(), method);
		}
		try {
			for (Object key : properties.keySet()) {
				String propertyName = (String) key;
				String setPropName = "set"+propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1);
				if (propertyName.startsWith(DRIVER_PROPERTY_PREFIX)) {
					String value = properties.getProperty(propertyName);
			  		driverProperties.setProperty(propertyName.substring(DRIVER_PROPERTY_PREFIX_LENGTH), value);
				} else if (methodMap.containsKey(setPropName)) {
					Method method = methodMap.get(setPropName);
					Object value = properties.get(propertyName);
					Class<?> targetType = method.getParameterTypes()[0];
					if (targetType == Integer.class || targetType == int.class) {
						value = Integer.valueOf(value.toString());
					} else if (targetType == Long.class || targetType == long.class) {
						value = Long.valueOf(value.toString());
					} else if (targetType == Boolean.class || targetType == boolean.class) {
						value = Boolean.valueOf(value.toString());
					}					
					method.invoke(dataSource, value);
				} else {
					throw new DataSourceException("Unknown DataSource property: " + propertyName);
				}
			}
			if (driverProperties.size() > 0) {
				methodMap.get("setDriverProperties").invoke(dataSource, driverProperties);
			}
		} catch (Exception e) {
			throw new DataSourceException(e);
		}
	}

	@Override
	public DataSource getDataSource() {
		return dataSource;
	}
	
	private ArrayList<Method> getDeclaredField(Object object){
		Class<?> clazz = object.getClass() ;
        ArrayList<Method> methodList = new ArrayList<>();
        Method[] mehtods = null;
		for(; clazz != Object.class ; clazz = clazz.getSuperclass()) {  
            try {  
            	mehtods = clazz.getDeclaredMethods() ; 
                for(Method m :mehtods){
                	if(m.getName().startsWith("set")){
                		methodList.add(m);
                	}
                }
            } catch (Exception e) {  
                //这里甚么都不要做！并且这里的异常必须这样写，不能抛出去。  
                //如果这里的异常打印或者往外抛，则就不会执行clazz = clazz.getSuperclass(),最后就不会进入到父类中了  
                  
            }   
        }
		return methodList; 
	}
}
